import inspect
import json
import logging
import time

import allure
import yaml
from appium.webdriver.webdriver import WebDriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC, expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from config import handle_black, BASE_DIR


class BasePage:
    _driver: WebDriver
    _params = {}

    def __init__(self, driver: WebDriver = None):
        self._driver = driver

    # 设置隐式等待
    def set_implicitly_wait(self, time=3):
        self._driver.implicitly_wait(time)

    # allure报告截图操作
    def allure_screenshot(self, name):
        allure.attach(self._driver.get_screenshot_as_png(), name, allure.attachment_type.PNG)

    # yaml 文件解析方法
    def steps(self, path):
        with open(path, encoding='utf8') as file:
            name = inspect.stack()[1].function
            logging.info(f'\n===> 运行函数名称为: {name}\n')
            steps = yaml.safe_load(file)[name]

        raw = json.dumps(steps)
        for key, value in self._params.items():
            raw = raw.replace(f'${{{key}}}', value)
            logging.info(f'\n===> 替换后raw: {raw}\n')
        steps = json.loads(raw)
        for step in steps:
            if "action" in step.keys():
                action = step['action']
                if 'click' == action:
                    logging.info(f'\n点击方法===> 定位方法: {step["by"]}, {step["locator"]}\n')
                    self.get_element(step['by'], step['locator']).click()
                if 'send' == action:
                    logging.info(f'\n发送方法===> 定位方法: {step["by"]}, {step["locator"]}\n')
                    self.input_clear_text(self.get_element(step['by'], step['locator']), step['value'])
                if 'exist' == action:
                    logging.info(f'\n判断元素是否存在===> 定位方法: {step["by"]}, {step["locator"]}\n')
                    is_exist = self.is_element_exist(step['by'], step['locator'])
                    logging.info(f'\n判断元素是否存在===> 结果为: {is_exist}\n')
                    return is_exist
                if 'swip' == action:
                    self.swipe_execute(step['direction'], step['times'])
                if 'swip_and_click' == action:
                    self.swipe_and_find_element((step['swip_by'], step['swip_locator']), (step['find_by'], step['find_locator']), step['direction'], step['time_out']).click()


    @handle_black
    def get_element(self, locator, value: str = None, time=5):
        """
        显示等待获取元素, 默认等待时间5s
        :param time: 等待时间
        :param value:   对应方式取值
        :param locator: 元组对象, type: 获取类型, By.Xpath  value: 对应方式取值
        :return: 获取元素对象
        """
        if isinstance(locator, tuple):
            logging.info(locator)
            wait = WebDriverWait(self._driver, time)
            element = wait.until(lambda x: x.find_element(*locator))
        else:
            wait = WebDriverWait(self._driver, time)
            element = wait.until(lambda x: x.find_element(locator, value))
        return element

    @handle_black
    def get_elements(self, locator, value: str = None, time=5):
        """
        显示等待获取元素列表, 默认等待时间5s
        :param time: 超时时间
        :param value:   对应方式取值
        :param locator: 元组对象, type: 获取类型, By.Xpath  value: 对应方式取值
        :return: 获取元素对象列表
        """
        if isinstance(locator, tuple):
            logging.info(locator)
            wait = WebDriverWait(self._driver, time)
            elements = wait.until(lambda x: x.find_elements(*locator))
        else:
            wait = WebDriverWait(self._driver, time)
            elements = wait.until(lambda x: x.find_elements(locator, value))
        return elements

    @handle_black
    def get_element_after_other_element(self, driver_element, locator, value: str = None, time=5):
        """
        在已有元素节点后, 显示等待获取元素, 默认等待时间5s
        :param time: 超时时间
        :param value: 对应方式取值
        :param driver_element: 已经获取到的元素 driver_element
        :param locator: 元组对象, type: 获取类型, By.Xpath  value: 对应方式取值
        :return: 获取元素对象
        """
        if isinstance(locator, tuple):
            logging.info(locator)
            wait = WebDriverWait(driver_element, time)
            element = wait.until(lambda x: x.find_element(*locator))
        else:
            wait = WebDriverWait(driver_element, time)
            element = wait.until(lambda x: x.find_element(locator, value))
        return element

    @handle_black
    def get_elements_after_other_element(self, driver_element, locator, value: str = None):
        """
        在已有元素节点后, 显示等待获取元素, 默认等待时间5s
        :param value: 对应方式取值
        :param driver_element: 已经获取到的元素 driver_element
        :param locator: 元组对象, type: 获取类型, By.Xpath  value: 对应方式取值
        :return: 获取元素对象列表
        """
        if isinstance(locator, tuple):
            logging.info(locator)
            wait = WebDriverWait(driver_element, 5)
            elements = wait.until(lambda x: x.find_elements(*locator))
        else:
            wait = WebDriverWait(driver_element, 5)
            elements = wait.until(lambda x: x.find_elements(locator, value))
        return elements

    def wait_can_click(self, locator):
        """
        显示等待元素可点击方法, 旁敲侧击方法
        :param locator:
        :return:
        """
        wait = WebDriverWait(self._driver, 5)
        element = wait.until(
            EC.element_to_be_clickable(locator)
        )
        return element

    def is_element_exist(self, locator, value: str = None, time_out=3):
        """
        判断页面元素是否存在
        :param time_out: 等待时间默认等2秒
        :param value: 对应方式取值
        :param locator: type_and_value: 元组对象, type: 获取类型, By.Xpath
        :return: 元素是否存在
        """
        try:
            if isinstance(locator, tuple):
                logging.info(locator)
                wait = WebDriverWait(self._driver, time_out)
                # lambda x: x.find_element(*locator)
                wait.until(
                    expected_conditions.presence_of_element_located(locator)
                )
            else:
                wait = WebDriverWait(self._driver, time_out)
                # lambda x: x.find_element(locator, value)
                wait.until(
                    expected_conditions.presence_of_element_located((locator, value))
                )
            return True
        except Exception as exc:
            # print(exc)
            return False

    def action_chains_click(self, element):
        ActionChains(self._driver).click(element).perform()

    @staticmethod
    def input_clear_text(element, text):
        """
        输入文字操作--先清空,后输入
        :param element: 元素对象
        :param text: 要输入的内容
        """
        element.clear()
        element.send_keys(text)

    def swipe_execute(self, direction, times=1, duration=1000):
        """
        执行滑动操作
        :param direction: 向上滑动:up, 向下:down, 向左:left, 向右:right
        :param times: 滑动次数
        :param duration: 滑动持续时间,默认2000ms
        """
        width = self._driver.get_window_size()['width']
        height = self._driver.get_window_size()['height']

        if direction == 'up':
            position = (width / 2, height * 0.9, width / 2, height * 0.1)
        elif direction == 'down':
            position = (width / 2, height * 0.1, width / 2, height * 0.9)
        elif direction == 'left':
            position = (width * 0.9, height / 2, width * 0.1, height / 2)
        elif direction == 'right':
            position = (width * 0.1, height / 2, width * 0.9, height / 2)
        else:
            position = (width / 2, height / 2, width / 2, height / 2)
            print('输入正确方向:向上:up, 向下:down, 向左:left, 向右:right')

        # 进入页面就滑动会报错
        time.sleep(0.5)

        for i in range(times):
            self._driver.swipe(*position, duration=duration)
            time.sleep(duration / 1000)

    def swipe_and_find_element(self, swipe_locator, find_locator, direction, duration=100, timeout=30):
        """
        边滑动边寻找某元素,找到后返回该元素,找不到返回False
        :param swipe_locator: 要滑动的目标 元组对象, type: 获取类型, By.Xpath  value: 对应方式取值
        :param direction: 滑动的方向 向上滑动:up, 向下:down, 向左:left, 向右:right
        :param find_locator: 想要查询的目标 元组对象, type: 获取类型, By.Xpath  value: 对应方式取值
        :param duration: 持续时间,默认500ms
        :param timeout: 滑动寻找设置超时时间, 超过设置时间后返回false, 默认30s
        :return: 找到:返回找到的元素, 找不到返回False
        """

        # 元素本身属性方位大小
        try:
            wait = WebDriverWait(self._driver, 2)
            swipe_element = wait.until(lambda x: x.find_element(*swipe_locator))

            ele_w = swipe_element.size['width']
            ele_h = swipe_element.size['height']
            ele_x = swipe_element.location['x']
            ele_y = swipe_element.location['y']

            if direction == 'up':
                position = (ele_x + ele_w / 2, ele_y + ele_h * 0.9, ele_x + ele_w / 2, ele_y + ele_h * 0.1)
            elif direction == 'down':
                position = (ele_x + ele_w / 2, ele_y + ele_h * 0.1, ele_x + ele_w / 2, ele_y + ele_h * 0.9)
            elif direction == 'left':
                position = (ele_x + ele_w * 0.9, ele_y + ele_h / 2, ele_x + ele_w * 0.1, ele_y + ele_h / 2)
            elif direction == 'right':
                position = (ele_x + ele_w * 0.1, ele_y + ele_h / 2, ele_x + ele_w * 0.9, ele_y + ele_h / 2)
            else:
                position = (ele_x + ele_w / 2, ele_y + ele_h / 2, ele_x + ele_w / 2, ele_y + ele_h / 2)
                logging.info('输入正确方向:向上:up, 向下:down, 向左:left, 向右:right')

            start_time = time.time()
            end_time = 0
            while True:
                # 记录下查找前页面资源, 通过对比页面资源退出死循环
                if int(end_time - start_time) < timeout:
                    if int(end_time - start_time) > 0:
                        # 大于0时在打印
                        logging.info(f'运行了{int(end_time - start_time)}s了')
                    page = self._driver.page_source
                    try:
                        fin_element = self._driver.find_element(*find_locator)
                        return fin_element
                    except Exception as exc:
                        logging.info('本屏幕没有找到该元素')
                        self._driver.swipe(*position, duration=duration)
                        time.sleep(duration / 1000)
                        if page == self._driver.page_source:
                            logging.warning('滑屏操作完成并且没有找到元素信息')
                            return False
                    finally:
                        end_time = time.time()
                else:
                    logging.warning(f'滑动寻找操作超过预设{timeout}s, 未找到该元素')
                    return False
        except Exception as exc:
            logging.warning(print('将要滑动的控件不存在, 修改获取控件方法后再试'))
            return False

    # 获取toast消息
    def get_toast(self, message, timeout=2):
        """
        根据部分信息,获取完整toast提示
        :param driver: 驱动
        :param message: 弹窗部分信息
        :param timeout: 获取toast的超时时间
        :return: 完整的toast消息
        """
        try:
            xpath = '//*[contains(@text, "{}")]'.format(message)
            wait = WebDriverWait(self._driver, timeout)
            element = wait.until(lambda x: x.find_element(By.XPATH, xpath))
            logging.info(element.text)
            return element.text
        except Exception:
            logging.debug('没有获取到 {} 相关弹窗提示信息'.format(message))
